package com.rizzo.android.content.http;

import com.google.common.collect.ImmutableMap;
import com.rizzo.analytics.domain.KeyType;
import com.rizzo.analytics.domain.application.Applications;
import com.rizzo.analytics.domain.errors.Errors;
import com.rizzo.analytics.domain.info.Info;
import com.rizzo.analytics.domain.performance.Performance;
import com.rizzo.analytics.domain.quota.Quotas;
import com.rizzo.android.content.http.util.HttpAccessUtil;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class StateHolder {

    private static final String APPLICATIONS_BASE_CONTEXT = "/v1/analytics/applications/";
    private static final String INFO_BASE_CONTEXT = "/v1/analytics/info/";
    private static final String PERFORMANCE_BASE_CONTEXT = "/v1/analytics/performance/";
    private static final String QUOTAS_BASE_CONTEXT = "/v1/analytics/quotas/";
    private static final String ERRORS_BASE_CONTEXT = "/v1/analytics/errors/";

    private static final String KEY_TYP_PARAMETER = "keyType";
    private static final String USER_PARAMETER = "user";
    public static final String WILDCARD = "*";

    private String host;
    private String username;
    private String password;
    private String acceptHeader;

    private Map<String, Info> applicationInfos = new HashMap<String, Info>();
    private Map<String, Performance> applicationPerformances = new HashMap<String, Performance>();
    private Map<String, Quotas> applicationQuotas = new HashMap<String, Quotas>();
    private Map<String, Errors> applicationErrors = new HashMap<String, Errors>();

    private ObjectMapper objectMapper;

    public StateHolder(String host, String username, String password, String acceptHeader) {
        this.host = host;
        this.username = username;
        this.password = password;
        this.acceptHeader = acceptHeader;
        this.objectMapper = new ObjectMapper();
    }

    public Applications getApplications() throws IOException {
        String applicationsJson = HttpAccessUtil.get(host, APPLICATIONS_BASE_CONTEXT,
                username, password, acceptHeader, null, null);
        return objectMapper.readValue(applicationsJson, Applications.class);
    }

    public Info getInfo(String application, String category, KeyType keyType) throws IOException {
        Map<String, String> infoQueryParameters = ImmutableMap.of(KEY_TYP_PARAMETER, keyType.name());
        String infoJson = HttpAccessUtil.get(host, INFO_BASE_CONTEXT + category,
                username, password, acceptHeader, application, infoQueryParameters);
        return objectMapper.readValue(infoJson, Info.class);
    }

    public Errors getErrors(String application, String errorCode) throws IOException {
        Map<String, String> errorsQueryParameters = ImmutableMap.of(USER_PARAMETER, username);
        String json = HttpAccessUtil.get(host, ERRORS_BASE_CONTEXT + errorCode, username, password, acceptHeader, application, errorsQueryParameters);
        return objectMapper.readValue(json, Errors.class);
    }

    public Quotas getQuotas(String application, String method) throws IOException {
        Map<String, String> quotasQueryParameters = ImmutableMap.of(method, WILDCARD);
        String json = HttpAccessUtil.get(host, QUOTAS_BASE_CONTEXT, username, password, acceptHeader, application, quotasQueryParameters);
        return objectMapper.readValue(json, Quotas.class);
    }

    public Performance getPerformance(String application, String category) throws IOException {
        Map<String, String> queryParameters = ImmutableMap.of();
        String json = HttpAccessUtil.get(host, PERFORMANCE_BASE_CONTEXT + category, username, password, acceptHeader, application, queryParameters);
        return objectMapper.readValue(json, Performance.class);
    }

    public Info updateInfo(String application) throws IOException {
        Info update = this.getInfo(application, WILDCARD, KeyType.ALL);
        Info applicationInfo = this.applicationInfos.get(application);
        if (applicationInfo != null) {
            applicationInfo.merge(update);
            return applicationInfo;
        } else {
            this.applicationInfos.put(application, update);
            return update;
        }
    }

    public Performance updatePerformance(String application) throws IOException {
        Performance update = getPerformance(application, WILDCARD);
        Performance applicationPerformance = applicationPerformances.get(application);
        if (applicationPerformance != null) {
            applicationPerformance.merge(update);
            return applicationPerformance;
        } else {
            this.applicationPerformances.put(application, update);
            return update;
        }
    }

    public Quotas updateQuotas(String application) throws IOException {
        Quotas update = getQuotas(application, WILDCARD);
        Quotas applicationQuota = applicationQuotas.get(application);
        if (applicationQuota != null) {
            applicationQuota.merge(update);
            return applicationQuota;
        } else {
            this.applicationQuotas.put(application, update);
            return update;
        }
    }

    public Errors updateErrors(String application) throws IOException {
        Info applicationInfo = this.applicationInfos.get(application);
        Set<String> errorCodes = applicationInfo.getErrorCodes();
        for (String errorCode : errorCodes) {
            Errors update = getErrors(application, errorCode);
            Errors applicationError = applicationErrors.get(application);
            if (applicationError != null) {
                applicationError.merge(update);
            } else {
                this.applicationErrors.put(application, update);
            }
        }
        return applicationErrors.get(application);
    }

}